function Engine() {
	const smallBlind = 10;
	const bigBlind = 2 * smallBlind;
	const nrOfPlayers = 9;
	
	var names = ["funkymonky", "CatFish", "John", "Ace", "TheJoker", "Littlemiss",
	             "BadBoy007", "BondJames", "DoS", "Ronin1", "master1337", "Jesus666"];
	var images = ["anon.png", "avatar1.jpg", "avatar2.jpg", "avatar3.jpg"];
	
	var evaluator = new Evaluator();
	
	var gameOver = true;
	
	var pot = 0;
	var maxBet = 0;
	var currentBet = 0;
	var step = bigBlind;
	
	var dealer = 0;
	var raiseMarker = nrOfPlayers + 1;
	var position = 0;
	
	var human = 0;
	var isHumanTurn = false;
	
	var round = 0;
	var stage = 0;
	
	var playersInGame = nrOfPlayers;
	var playersInRound = nrOfPlayers;
	var playersBetting = nrOfPlayers;
	
	var bestHand = 9999;
	var winners = [];
	
	var deck = null;
	var players = new Array(nrOfPlayers);
	var commCards = new Array(5);
	
	
	
	function nextPosition() {
		position++;
		if (position >= nrOfPlayers) {
			position %= nrOfPlayers;
		}
	}
	
	function nextDealer() {
		position = dealer;
		nextPosition();
		
		while (!players[position].isInGame()) {
			nextPosition();
		}
		
		moveDealersButton(dealer, position);
		dealer = position;
	}
	
	
	
	this.startGame = function (_name, _image) {
		console.log("New game starts");
		gameOver = false;
		
		round = 0;
		
		setSB(smallBlind);
		setBB(bigBlind);
		
		human = Math.floor(Math.random() * nrOfPlayers);
		names.shuffle();
		var l = 0;
		
		for (var i = 0; i < nrOfPlayers; i++) {
			if (i === human) {
				players[i] = new Player(_name, _image);
			} else {
				if (names[l] === _name) {
					l++;
				}
				
				players[i] = new Player(names[l], images[Math.floor(Math.random() * images.length)]);
				l++;
			}
			
			setPlayer(i, players[i]);
			
            if(i !== human){
                markAI(i);
            }
		}
		
		chatBot();
		
		dealer = Math.floor(Math.random() * nrOfPlayers);
		setDealerButton(dealer);
		
		setTimeout(startRound, 500);
	}
	
	function endGame() {
		for (var i = 0; i < nrOfPlayers; i++) {
			if (players[i].isInGame()) {
				enableWinner(i);
			}
		}
		
		gameOver = true;
		console.log("End of game");
	}
	
	function startRound() {
		round++;
		console.log("Start of Round " + round);
		
		if (!gameOver && playersInGame > 1) {
			bestHand = 9999;
			winners = [];
			deck = new Deck();
			playersInRound = playersInGame;
			playersBetting = playersInGame;
			stage = 0;
			step = bigBlind;
			console.log(players[dealer].getName() + " is Dealer");
			setRaise(step);
			setPot(pot);
			setTimeout(advanceStage, 500);
		} else {
			endGame();
		}
	}
	
	function endRound() {
		isHumanTurn = false;
		
		var winnings = Math.round(pot / playersInRound);
		for (var i = 0; i < nrOfPlayers; i++) {
			if (players[i].isInRound()) {
				players[i].collectWinnings(winnings);
				enableWinner(i);
				console.log(players[i].getName() + " wins " + winnings + "$");
			}
			
			if (players[i].isInGame() && players[i].getCash() === 0) {
				console.log(players[i].getName() + " leaves");
				players[i].leave();
				playersInGame--;
			}
			
			foldCards(i);
			players[i].reset();
			updatePlayer(i, players[i]);
			removeBet(i);
		}
		
		removeFTR();
		
		pot = 0;
		nextDealer();
		
		console.log("End of Round " + round);
		setTimeout(startRound, 2000);
	}
	
	function advanceStage() {
		if (gameOver) {
			return;
		}
		
		switch (stage) {
			case 0:
				console.log("Pre-flop");
				deal(0);
				step = bigBlind;
				maxBet = 4 * step;
				placeBlinds();
				setPot(pot);
				currentBet = bigBlind;
				raiseMarker = nrOfPlayers + 1;
				setTimeout(betting, 2500);
				break;
			case 1:
				console.log("Flop");
				deck.getCard();
				burnCard();
				
				var images = [];
				for (var i = 0; i < 3; i++) {
					commCards[i] = deck.getCard();
					images[i] = commCards[i].getImage();
					console.log((i + 1) + ". flop card: " + commCards[i].getName());
				}
				setTimeout(function () {animateFlop(images)}, 1000);
				
				maxBet = currentBet + 4 * step;
				position = (dealer) % nrOfPlayers;
				raiseMarker = nrOfPlayers + 1;
				setTimeout(betting, 2000);
				break;
			case 2:
				console.log("Turn");
				deck.getCard();
				burnCard();
				
				commCards[3] = deck.getCard();
				console.log("Turn card: " + commCards[3].getName());
				setTimeout(function () {animateTurn(commCards[3].getImage())}, 1000);
				
				step = 2 * bigBlind;
				setRaise(step);
				maxBet = currentBet + 4 * step;
				position = (dealer + 1) % nrOfPlayers;
				raiseMarker = nrOfPlayers + 1;
				setTimeout(betting, 2000);
				break;
			case 3:
				console.log("River");
				deck.getCard();
				burnCard();
				
				commCards[4] = deck.getCard();
				console.log("River card: " + commCards[4].getName());
				setTimeout(function () {animateRiver(commCards[4].getImage())}, 1000);
				
				maxBet = currentBet + 4 * step;
				position = (dealer + 1) % nrOfPlayers;
				raiseMarker = nrOfPlayers + 1;
				setTimeout(betting, 2000);
				break;
			case 4:
				console.log("Showdown");
				position = raiseMarker % nrOfPlayers;
				showdown(0);
				break;
			default:
				alert("Funky stuff happening with game stages!");
		}
		
		stage++;
	}
	
	function placeBlinds() {
		position = dealer;
		nextPosition();
		
		while (!players[position].isInGame()) {
			nextPosition();
		}
		if (players[position].getCash() > smallBlind) {
			players[position].makeBet(smallBlind);
		} else {
			players[position].allin();
			playersBetting--;
		}
		updatePlayer(position, players[position]);
		pot = players[position].getBet();
		nextPosition();
		
		while (!players[position].isInGame()) {
			nextPosition();
		}
		if (players[position].getCash() > bigBlind) {
			players[position].makeBet(bigBlind);
		} else {
			players[position].allin();
			playersBetting--;
		}
		updatePlayer(position, players[position]);
		pot += players[position].getBet();
		nextPosition();
	}
	
	function deal(n) {
		if (n < 2 * nrOfPlayers) {
			var p = (dealer + 1 + n) % nrOfPlayers;
			disableWinner(p);
			
			if (players[p].isInGame()) {
				var tempCard = deck.getCard();
				players[p].addCard(tempCard);
				console.log(players[p].getName() + " gets " + tempCard.getName());
				if (p === human) {
					dealCard(p, tempCard.getImage(), Math.floor(n / nrOfPlayers));
				} else {
					dealCard(p, "B.png", Math.floor(n / nrOfPlayers));
				}
			}
			
			setTimeout(function() {deal(n + 1)}, 100);
		}
	}
	
	function betting() {
		if (gameOver) {
			return;
		}
		
		if (playersInRound > 1) {
			if (playersBetting > 0) {
				if (position !== raiseMarker) {
					if (players[position].isInRound() && players[position].isBetting()) {
						if (raiseMarker > nrOfPlayers) {
							raiseMarker = position;
						}
						
						activatePlayer(position);
						if (position !== human) {
							setTimeout(function () {makeMove(getOptions(position))}, 500);
						} else {
							enableButtons(getOptions(position));
						}
					} else {
						nextPosition();
						betting();
					}
				} else {
					advanceStage();
				}
			} else {
				advanceStage();
			}
		} else {
			endRound();
		}
	}
	
	function getOptions(pos) {
		var options = [1];
		var bet = players[pos].getBet();
		var cash = players[pos].getCash();
		var assets = players[pos].getAssets();
		
		if (bet === currentBet) {
			options[options.length] = 2;
		} else if (bet < currentBet && currentBet < assets) {
			options[options.length] = 3;
		}
		
		if (currentBet < maxBet) {
			if (currentBet + step < assets) {
				options[options.length] = 4;
			} else {
				options[options.length] = 5;
			}
		}
		
		if (options.length < 2) {
			options[1] = 5;
		}
		
		return options;
	}
	
	function makeMove(options) {
		var move = Math.floor(Math.random() * options.length);
		
		switch (options[move]) {
			case 1:
				privateFold();
				break;
			case 2:
				privateCheck();
				break;
			case 3:
				privateCall();
				break;
			case 4:
				privateRaise();
				break;
			case 5:
				privateAllin();
				break;
			default:
				alert("Funky stuff happening with " + players[position].getName() + "'s moves!");
		}
	}
	
	function enableButtons(options) {
		for (var i = 0; i < options.length; i++) {
			switch (options[i]) {
				case 1:
					enableFold();
					break;
				case 2:
					enableCheck();
					break;
				case 3:
					enableCall();
					break;
				case 4:
					enableRaise();
					break;
				case 5:
					enableAllin();
					break;
				default:
					alert("Funky stuff happening with buttons!");
			}
		}
		
		isHumanTurn = true;
	}
	
	function showdown(n) {
		if (n  < nrOfPlayers) {
			if (players[position].isInRound()) {
				var handValue = evaluateHand();
				
				if (handValue < bestHand) {
					bestHand = handValue;
					for (var j = 0; j < winners.length; j++) {
						players[winners[j]].lose();
						playersInRound--;
					}
					
					winners = [position];
				} else if (handValue === bestHand) {
					winners[winners.length] = position;
				} else {
					players[position].lose();
					playersInRound--;
				}
				
				if (position !== human) {
					var hand = players[position].getHand();
					showHand(position, hand[0].getImage(), hand[1].getImage());
				}
				
				nextPosition();
				setTimeout(function() {showdown(n + 1)}, 500);
			} else {
				nextPosition()
				showdown(n + 1);
			}
		} else {
			setTimeout(endRound, 2000);
		}
	}
	
	function evaluateHand() {
		var cards = players[position].getHand().concat(commCards);
		var best = 9999;
		
		for (var i = 0; i < cards.length - 4; i++) {
			for (var j = i + 1; j < cards.length - 3; j++) {
				for (var k = j + 1; k < cards.length - 2; k++) {
					for (var l = k + 1; l < cards.length - 1; l++) {
						for (var m = l + 1; m < cards.length; m++) {
							var value = evaluator.evaluate(cards[i], cards[j],
									cards[k], cards[l], cards[m]);
							
							if (value < best) {
								best = value;
							}
						}
					}
				}
			}
		}
		
		return best;
	}
	
	
	
	function foldCards(pos) {
		players[pos].fold();
		retrieveCards(pos);
	}
	
	function privateFold() {
		console.log(players[position].getName() + " folds");
		foldCards(position);
		playersBetting--;
		playersInRound--;
		deactivatePlayer(position);
		nextPosition();
		setTimeout(betting, 200);
	}
	
	function privateCheck() {
		console.log(players[position].getName() + " checks");
		deactivatePlayer(position);
		nextPosition();
		setTimeout(betting, 200);
	}
	
	function privateCall() {
		console.log(players[position].getName() + " calls");
		var temp = players[position].getBet();
		players[position].makeBet(currentBet);
		pot += players[position].getBet() - temp;
		setPot(pot);
		updatePlayer(position, players[position]);
		deactivatePlayer(position);
		nextPosition();
		setTimeout(betting, 200);
	}
	
	function privateRaise() {
		if (currentBet < maxBet) {
			console.log(players[position].getName() + " raises");
			var temp = players[position].getBet();
			players[position].makeBet(currentBet + step);
			currentBet = players[position].getBet();
			pot += currentBet - temp;
			setPot(pot);
			updatePlayer(position, players[position]);
			deactivatePlayer(position);
			raiseMarker = position;
			nextPosition();
		}
		setTimeout(betting, 200);
	}
	
	function privateAllin() {
		console.log(players[position].getName() + " goes all-in");
		var temp = players[position].getBet();
		players[position].allin();
		var bet = players[position].getBet();
		playersBetting--;
		
		if (bet > currentBet) {
			currentBet = bet;
			raiseMarker = position;
		}
		pot += bet - temp;
		setPot(pot);
		updatePlayer(position, players[position]);
		deactivatePlayer(position);
		nextPosition();
		setTimeout(betting, 200);
	}
	
	this.fold = function () {
		if (isHumanTurn) {
			isHumanTurn = false;
			privateFold();
		}
		
		disableButtons();
	};
	
	this.check = function () {
		if (isHumanTurn) {
			isHumanTurn = false;
			privateCheck();
		}
		
		disableButtons();
	};
	
	this.call = function () {
		if (isHumanTurn) {
			isHumanTurn = false;
			privateCall();
		}
		
		disableButtons();
	};
	
	this.raise = function () {
		if (isHumanTurn) {
			isHumanTurn = false;
			privateRaise();
		}
		
		disableButtons();
	};
	
	this.allin = function () {
		if (isHumanTurn) {
			isHumanTurn = false;
			privateAllin();
		}
		
		disableButtons();
	};
	
	this.leaveGame = function () {
		endGame();
	}
	
	
	
	function chatBot() {
		if (!gameOver) {
			var phrases = ["OMG",
			               "double rainbow across the sky",
			               "Is it a bluff? Or maybe a double bluff?",
			               "Are you kidding?",
			               "imma gonna be da winna",
			               "lol",
			               ":D:D:D:D:D:D",
			               ":P",
			               "Anybody wants a cookie?"];
			
			var phrase = Math.floor(Math.random() * phrases.length);
			var id = Math.floor(Math.random() * nrOfPlayers);
			while (id === human) {
				id = Math.floor(Math.random() * nrOfPlayers);
			}
			chatMessage(players[id].getName(), phrases[phrase]);
			
			setTimeout(chatBot, Math.floor(Math.random() * 9500 + 500));
		}
	}
}